package com.sz.virtualbank.service.user.impl;

import com.sz.virtualbank.common.exception.BusinessException;
import com.sz.virtualbank.common.utils.CheckUtil;
import com.sz.virtualbank.common.utils.JwtUtil;
import com.sz.virtualbank.common.utils.PasswordUtil;
import com.sz.virtualbank.dao.entity.User;
import com.sz.virtualbank.dao.entity.UserToken;
import com.sz.virtualbank.dao.mapper.UserMapper;
import com.sz.virtualbank.dao.mapper.UserTokenMapper;
import com.sz.virtualbank.service.user.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 用户服务实现类
 * 
 * @author Claude
 */
@Slf4j
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;
    
    @Autowired
    private UserTokenMapper userTokenMapper;
    
    // 验证码缓存，实际项目中应该使用Redis等缓存服务
    private static final Map<String, String> CODE_CACHE = new ConcurrentHashMap<>();
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String register(String username, String password, String phone, String validCode) {
        // 参数校验
        CheckUtil.Biz.INSTANCE
                .notEmpty(username, "用户名不能为空")
                .notEmpty(password, "密码不能为空")
                .notEmpty(phone, "手机号不能为空")
                .notEmpty(validCode, "验证码不能为空");
        
        // 验证码校验
        String cachedCode = CODE_CACHE.get(phone);
        CheckUtil.Biz.INSTANCE
                .notNull(cachedCode, "验证码已过期")
                .isTrue(cachedCode.equals(validCode), "验证码不正确");
        
        // 检查用户名和手机号是否已存在
        User existUser = userMapper.selectByUsername(username);
        CheckUtil.Biz.INSTANCE.isTrue(existUser == null, "用户名已存在");
        
        existUser = userMapper.selectByPhone(phone);
        CheckUtil.Biz.INSTANCE.isTrue(existUser == null, "手机号已注册");
        
        // 密码加密
        String salt = PasswordUtil.generateSalt();
        String encryptedPassword = PasswordUtil.encrypt(password, salt);
        
        // 创建用户
        User user = new User();
        user.setUsername(username);
        user.setPassword(encryptedPassword);
        user.setPhone(phone);
        user.setBalance(BigDecimal.ZERO);
        user.setFrozenAmount(BigDecimal.ZERO);
        user.setStatus(1);
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        user.setDeleted(0);
        user.setSalt(salt);
        
        userMapper.insert(user);
        
        // 清除验证码缓存
        CODE_CACHE.remove(phone);
        
        // 生成Token并返回
        return createToken(user.getId());
    }
    
    @Override
    public String login(String username, String password) {
        // 参数校验
        CheckUtil.Biz.INSTANCE
                .notEmpty(username, "用户名不能为空")
                .notEmpty(password, "密码不能为空");
        
        // 查询用户
        User user = null;
        if (username.matches("^1[3-9]\\d{9}$")) {
            // 手机号登录
            user = userMapper.selectByPhone(username);
        } else {
            // 用户名登录
            user = userMapper.selectByUsername(username);
        }
        
        CheckUtil.Biz.INSTANCE.notNull(user, "用户不存在");
        CheckUtil.Biz.INSTANCE.isTrue(user.getStatus() == 1, "账号已被禁用");
        
        // 验证密码
        boolean passwordMatch = PasswordUtil.verify(password, user.getSalt(), user.getPassword());
        CheckUtil.Biz.INSTANCE.isTrue(passwordMatch, "密码不正确");
        
        // 更新最后登录时间
        user.setLastLoginTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        userMapper.updateById(user);
        
        // 生成Token并返回
        return createToken(user.getId());
    }
    
    @Override
    public void logout(String token) {
        // 参数校验
        CheckUtil.Biz.INSTANCE.notEmpty(token, "令牌不能为空");
        
        // 删除令牌
        userTokenMapper.deleteByToken(token);
    }
    
    @Override
    public User getCurrentUser(String token) {
        // 参数校验
        CheckUtil.Biz.INSTANCE.notEmpty(token, "令牌不能为空");
        
        // 查询令牌
        UserToken userToken = userTokenMapper.selectByToken(token);
        CheckUtil.Biz.INSTANCE.notNull(userToken, "令牌无效或已过期");
        
        // 检查令牌是否过期
        if (userToken.getExpireTime().isBefore(LocalDateTime.now())) {
            userTokenMapper.deleteByToken(token);
            throw BusinessException.of(4001, "令牌已过期，请重新登录");
        }
        
        // 查询用户
        User user = userMapper.selectById(userToken.getUserId());
        CheckUtil.Biz.INSTANCE.notNull(user, "用户不存在");
        CheckUtil.Biz.INSTANCE.isTrue(user.getStatus() == 1, "账号已被禁用");
        
        // 更新令牌最后使用时间
        userToken.setLastUseTime(LocalDateTime.now());
        userTokenMapper.updateById(userToken);
        
        return user;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public User updateUserInfo(User user) {
        // 参数校验
        CheckUtil.Biz.INSTANCE.notNull(user, "用户信息不能为空");
        CheckUtil.Biz.INSTANCE.notNull(user.getId(), "用户ID不能为空");
        
        // 查询用户
        User existUser = userMapper.selectById(user.getId());
        CheckUtil.Biz.INSTANCE.notNull(existUser, "用户不存在");
        
        // 用户名唯一性校验
        if (StringUtils.isNotBlank(user.getUsername()) && !user.getUsername().equals(existUser.getUsername())) {
            User usernameUser = userMapper.selectByUsername(user.getUsername());
            CheckUtil.Biz.INSTANCE.isTrue(usernameUser == null, "用户名已存在");
        }
        
        // 手机号唯一性校验
        if (StringUtils.isNotBlank(user.getPhone()) && !user.getPhone().equals(existUser.getPhone())) {
            User phoneUser = userMapper.selectByPhone(user.getPhone());
            CheckUtil.Biz.INSTANCE.isTrue(phoneUser == null, "手机号已注册");
        }
        
        // 保留敏感字段
        user.setPassword(null);
        user.setSalt(null);
        user.setBalance(null);
        user.setFrozenAmount(null);
        user.setStatus(null);
        user.setCreateTime(null);
        user.setDeleted(null);
        
        // 设置更新时间
        user.setUpdateTime(LocalDateTime.now());
        
        // 更新用户信息
        userMapper.updateById(user);
        
        // 返回更新后的用户信息
        return userMapper.selectById(user.getId());
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean changePassword(Long userId, String oldPassword, String newPassword) {
        // 参数校验
        CheckUtil.Biz.INSTANCE
                .notNull(userId, "用户ID不能为空")
                .notEmpty(oldPassword, "原密码不能为空")
                .notEmpty(newPassword, "新密码不能为空");
        
        // 查询用户
        User user = userMapper.selectById(userId);
        CheckUtil.Biz.INSTANCE.notNull(user, "用户不存在");
        
        // 验证原密码
        boolean passwordMatch = PasswordUtil.verify(oldPassword, user.getSalt(), user.getPassword());
        CheckUtil.Biz.INSTANCE.isTrue(passwordMatch, "原密码不正确");
        
        // 新密码不能与原密码相同
        CheckUtil.Biz.INSTANCE.isTrue(!oldPassword.equals(newPassword), "新密码不能与原密码相同");
        
        // 生成新的盐值和加密密码
        String newSalt = PasswordUtil.generateSalt();
        String encryptedPassword = PasswordUtil.encrypt(newPassword, newSalt);
        
        // 更新密码
        return userMapper.updatePassword(userId, encryptedPassword, newSalt) > 0;
    }
    
    @Override
    public void sendVerificationCode(String phone) {
        // 参数校验
        CheckUtil.Biz.INSTANCE.notEmpty(phone, "手机号不能为空");
        
        // 生成验证码
        String code = generateVerificationCode();
        
        // 发送验证码
        // TODO 对接短信服务
        log.info("向手机号 {} 发送验证码：{}", phone, code);
        
        // 缓存验证码，5分钟有效期
        CODE_CACHE.put(phone, code);
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String resetPassword(String phone, String validCode, String newPassword) {
        // 参数校验
        CheckUtil.Biz.INSTANCE
                .notEmpty(phone, "手机号不能为空")
                .notEmpty(validCode, "验证码不能为空")
                .notEmpty(newPassword, "新密码不能为空");
        
        // 验证码校验
        String cachedCode = CODE_CACHE.get(phone);
        CheckUtil.Biz.INSTANCE
                .notNull(cachedCode, "验证码已过期")
                .isTrue(cachedCode.equals(validCode), "验证码不正确");
        
        // 查询用户
        User user = userMapper.selectByPhone(phone);
        CheckUtil.Biz.INSTANCE.notNull(user, "用户不存在");
        
        // 生成新的盐值和加密密码
        String newSalt = PasswordUtil.generateSalt();
        String encryptedPassword = PasswordUtil.encrypt(newPassword, newSalt);
        
        // 更新密码
        int count = userMapper.updatePassword(user.getId(), encryptedPassword, newSalt);
        if (count <= 0) {
            throw BusinessException.of(5000, "重置密码失败");
        }
        
        // 清除验证码缓存
        CODE_CACHE.remove(phone);
        
        // 清除原有的令牌
        userTokenMapper.deleteByUserId(user.getId());
        
        // 生成新的令牌并返回
        return createToken(user.getId());
    }
    
    /**
     * 创建令牌
     * 
     * @param userId 用户ID
     * @return 令牌
     */
    private String createToken(Long userId) {
        // 生成令牌
        String token = UUID.randomUUID().toString().replace("-", "");
        
        // 删除用户之前的令牌
        userTokenMapper.deleteByUserId(userId);
        
        // 创建新的令牌记录
        UserToken userToken = new UserToken();
        userToken.setUserId(userId);
        userToken.setToken(token);
        userToken.setExpireTime(LocalDateTime.now().plusHours(2)); // 2小时有效期
        userToken.setCreateTime(LocalDateTime.now());
        userToken.setLastUseTime(LocalDateTime.now());
        
        userTokenMapper.insert(userToken);
        
        return token;
    }
    
    /**
     * 生成6位数字验证码
     * 
     * @return 验证码
     */
    private String generateVerificationCode() {
        return String.format("%06d", (int) (Math.random() * 1000000));
    }
}
